package com.example.oauth.tracing;

import com.example.oauth.tracing.config.JaegerConfigurationExtraProperties;
import com.example.oauth.utils.SpringContextUtil;
import com.google.common.collect.ImmutableMap;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
public class OwlTraceLog {

    /**
     * 默认最大为1K
     */
    private static final int DEFAULT_MAX_CONTENT_SIZE = 10240;

    /**
     * 记录open trace日志
     *
     * @param key
     * @param value
     */
    public static void log(String key, String value) {
        log(GlobalTracer.get(), key, value);
    }

    /**
     * 记录open trace日志
     *
     * @param tracer
     * @param key
     * @param value
     */
    public static void log(Tracer tracer, String key, String value) {
        if (tracer == null) {
            return;
        }
        log(tracer.activeSpan(), key, value);
    }

    /**
     * 记录open trace日志
     *
     * @param span
     * @param key
     * @param value
     */
    public static void log(Span span, String key, String value) {
        Map<String, String> fields = new LinkedHashMap<>(1);
        fields.put(key, value);
        log(span, fields);
    }

    /**
     * 记录open trace日志
     *
     * @param span
     * @param fields
     */
    public static void log(Span span, Map<String, String> fields) {
        try {
            if (span == null) {
                return;
            }
            if (!MapUtils.isEmpty(fields)) {
                // do content check and limit
                Map<String, String> updatedFields = limitContentSize(fields);

                span.log(updatedFields);
            }
        } catch (Exception e) {
            log.error("jaeger_trace_log_failed", e);
        }
    }

    /**
     * 记录open trace日志
     *
     * @param span
     * @param throwable
     */
    public static void error(Span span, Throwable throwable) {
        error(span, null, throwable);
    }

    /**
     * 记录open trace日志
     *
     * @param span
     * @param fields
     * @param throwable
     */
    public static void error(Span span, Map<String, String> fields, Throwable throwable) {
        try {
            if (span == null || throwable == null) {
                return;
            }
            if (fields == null) {
                fields = new LinkedHashMap<>(3);
            }
            // fill exception messages
            if (!fields.containsKey("event")) {
                fields.put("event", Tags.ERROR.getKey());
            }
            if (!fields.containsKey("message")) {
                fields.put("message", StringUtils.defaultString(throwable.getMessage(), throwable.getClass().getSimpleName()));
            }
            if (!fields.containsKey("stack")) {
                fields.put("stack", throwable.getMessage());
            }

            Tags.ERROR.set(span, true);
            log(span, fields);
        } catch (Exception e) {
            log.error("jaeger_trace_error_failed", e);
        }
    }

    private static Map<String, String> limitContentSize(Map<String, String> fields) {
        // 兼容不支持修改操作的Map
        Map<String, String> updatedFields = fields;
        if (fields instanceof ImmutableMap) {
            updatedFields = new LinkedHashMap<>(fields.size());
            for (Map.Entry<String, String> entry : fields.entrySet()) {
                updatedFields.put(entry.getKey(), entry.getValue());
            }
        }

        // 限制trace日志长度
        int maxContentSize = getMaxContentSize();
        for (Map.Entry<String, String> entry : updatedFields.entrySet()) {
            if (!StringUtils.isEmpty(entry.getValue())) {
                int size = entry.getValue().getBytes(Charset.defaultCharset()).length;
                if (size > maxContentSize) {
//                    String defaultStr = String.format("log content size %s > %s", size, maxContentSize);
                    String defaultStr = StringUtils.abbreviate(entry.getValue(), maxContentSize);
                    entry.setValue(defaultStr);
                }
            } else {
                entry.setValue(StringUtils.EMPTY);
            }
        }

        return updatedFields;
    }

    private static int getMaxContentSize() {
        int maxContentSize = DEFAULT_MAX_CONTENT_SIZE;
        JaegerConfigurationExtraProperties config = SpringContextUtil.getBean(JaegerConfigurationExtraProperties.class);
        if (config != null) {
            maxContentSize = config.getMaxLogContentSize();
        }
        return maxContentSize;
    }

    /**
     * 记录open trace标签
     *
     * @param key
     * @param value
     */
    public static void setTag(String key, String value) {
        setTag(GlobalTracer.get(), key, value);
    }

    /**
     * 记录open trace标签
     *
     * @param tracer
     * @param key
     * @param value
     */
    public static void setTag(Tracer tracer, String key, String value) {
        if (tracer == null) {
            return;
        }
        setTag(tracer.activeSpan(), key, value);
    }

    /**
     * 记录open trace标签
     *
     * @param span
     * @param key
     * @param value
     */
    public static void setTag(Span span, String key, String value) {
        try {
            if (span == null) {
                return;
            }
            span.setTag(key, value);
        } catch (Exception e) {
            log.error("jaeger_trace_tag_failed", e);
        }
    }

}
